import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
import { UnauthorizedException } from '@nestjs/common';
import { ExtractJwt, Strategy } from 'passport-jwt';
import type { StrategyOptions } from 'passport-jwt';
import { RedisService } from 'libs/db/src';
import type { Request } from 'express';
import { UserService } from 'src/modules/system/user/user.service';

// 导出JwtStrategy类，继承自PassportStrategy
export class JwtStrategy extends PassportStrategy(Strategy) {
  // 构造函数，注入userService、configService、redisService
  constructor(
    private readonly userService: UserService,
    private readonly configService: ConfigService,
    private readonly redisService: RedisService
  ) {
    // 调用父类构造函数，传入配置参数
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), // 从请求头中提取JWT
      secretOrKey: configService.get('JWT_SECRET') ?? 'jckj_secret', // 设置JWT密钥
      passReqToCallback: true // 将请求对象传递给回调函数
    } as StrategyOptions);
  }

  // 验证函数，接收请求对象和JWT负载
  async validate(req: Request, payload: any) {
    // 从请求头中提取JWT
    const token = ExtractJwt.fromAuthHeaderAsBearerToken()(req);
    // 根据JWT负载中的userId查找用户
    const existUser = await this.userService.findByUserId(payload.userId);

    // 从redis中获取缓存中的token
    const cacheToken = await this.redisService.get(payload.userId);

    // 如果缓存中的token不存在，抛出未授权异常
    if (!cacheToken) throw new UnauthorizedException('Token已过期！');
    // 如果缓存中的token与请求中的token不一致，抛出未授权异常
    if (cacheToken !== token) throw new UnauthorizedException('Token已失效！');
    // 如果用户不存在，抛出未授权异常
    if (!existUser) throw new UnauthorizedException('用户不存在！');

    // 将token存入redis，设置过期时间
    await this.redisService.set(
      `token_${existUser.userId}`,
      token,
      this.configService.get('JWT_EXPIRES_IN') ?? 3600
    );
    // 返回用户信息
    return existUser;
  }
}

//#region jwt策略
// 需要将passReqToCallback设置为true，作用是将请求传递给下面的validate()函数，可以看到，③处的validate()函数接受的第一个参数就是req:Request，否则的话validate()函数是拿不到请求的，也就不能从请求头中拿到 JWT

// 作者：bald3r
// 链接：https://juejin.cn/post/7260308502031433786
// 来源：稀土掘金
// 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。

//#endregion
